package com.woniuxy.article.service.impl;


//import com.github.pagehelper.PageHelper;
//import com.github.pagehelper.PageInfo;
import cn.hutool.core.util.BooleanUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hc.PageBean.PageBean;
import com.hc.entity.Paper;
import com.hc.entity.Shop;
import com.hc.vo.AgreeVo;
import com.woniuxy.article.mapper.PaperMapper;
import com.woniuxy.article.service.ArticleService;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *@ClassName ArticleServiceImpl
 *@Description
 *@Author TJ
 *@Date 2022/10/20 9:33
 *@Version 1.0
 **/
@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    PaperMapper paperMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public List<Shop> getByMessage(String message) {
        return null;
    }

    /**
     * 根据用户id获取用户的所有文章
     * @param id
     * @return
     */
    @Override
    public List<Paper> getPaperByUserId(int id) {
        List<Paper> paperMappers = paperMapper.selectAllByUserId(id);
        return paperMappers;
    }

    @Override
    public Paper getPaperByPaperId(int id) {
        Paper paper = paperMapper.selectByPaperId(id);
        return paper;
    }


    //解决缓存穿透 缓存雪崩  缓存击穿
    @Override
    public PageBean<Paper> selectByPage(int pageSize, int pageNum) throws Exception {
        if(pageNum == 0){
            pageNum=1;
        }
        if(pageSize == 0){
            pageSize=10;
        }
        //1.查询缓存是否有（key=pageNum+pageSize）
        String key =pageNum+"分页"+pageSize;
        //2.没有进行查询，查询完毕后存入缓存（设置过期时间，保证数据变化时尽量检测到）
        Boolean member = stringRedisTemplate.hasKey(key);
        PageBean pageBean = new PageBean();
        //不存在缓存，查询
        if(!member){
            //解决缓存击穿问题  热点key失效，并发请求同时请求数据库并进行redis重建  //使用互斥锁实现：利用setnx互斥特性
            //获取锁
            String lock="分页数据redis重建：当前页"+pageNum+"分页大小："+pageSize;
            try {
                boolean flog = tryLock(lock);
                //获取锁失败
                if(!flog){
                    //获取锁失败的线程，进行休眠。休眠时间要比重建redis的key的时间大
                    Thread.sleep(10);
                    //回调，再次查询。此次可能redis的key已经重建。返回保证不会向下继续执行
                    return selectByPage(pageSize,pageNum);
                }

                PageHelper.startPage(pageNum, pageSize);
                List<Paper> papers = paperMapper.selectByPaper();
                //模拟重建延时
                Thread.sleep(200);
                //用PageInfo对结果进行包装
                PageInfo pageInfo = new PageInfo(papers);
                pageBean = new PageBean<>(pageInfo);
                //如果缓存穿透，也即数据库查出的数据为空 解决//设置一个空的缓存（设置较短的过期时间）
                //数据查询为空，存在缓存穿透问题
                if(pageInfo.getList()==null){
                    //设置一个空的缓存,过期时间为2分钟
                    stringRedisTemplate.opsForValue().set(key, "", 2, TimeUnit.SECONDS);

                    //不存在缓存穿透问题
                }else {
                    //将查询出的信息存到缓存
                    String s = JSON.toJSONString(pageBean);
                    //向redis里存入数据和设置缓存时间
                    int timeout=6*60*10;
                    //Math.random() 随机取（0-1）的数
                    int i = (int) (Math.random() *50 );  //随机获取1-5   过期时间设置为不等，防止缓存雪崩  TimeUnit.SECONDS秒
                    stringRedisTemplate.opsForValue().set(key, s, timeout+i, TimeUnit.SECONDS);
                }
            }catch (Exception e){
                e.printStackTrace();
                throw new Exception(e);
            }finally {
                //释放锁
                unLock(lock);
            }
            //返回
            return pageBean;

        //3.有查出缓存
        }else {
            String s = stringRedisTemplate.opsForValue().get(key);
            pageBean = JSON.parseObject(s, PageBean.class);
            return pageBean;
        }

    }



//        pageBean.setData(papers);
//        System.out.println("总条数"+pageInfo.getTotal());
//        System.out.println("当前页"+pageInfo.getPageNum());
//        System.out.println("总页数"+pageInfo.getPages());
//        System.out.println("当前页总条数数"+pageInfo.getList().size());
//        System.out.println(pageInfo.getList());
//        pageBean.setCurrPage(pageInfo.getPageNum());
//        pageBean.setTotalPages(pageInfo.getPages());

    //利用setnx尝试获取锁，成功获取锁返回true 失败返回false
    private boolean tryLock(String key){
        //setnx设置成功，视为获取到锁。过期时间作为一个保险，防止出现异常无法释放锁
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        //防止自动拆箱导致空值
        return BooleanUtil.isTrue(flag);
    }
    //释放锁
    private void unLock(String key){
        stringRedisTemplate.delete(key);
    }

    @Override
    public boolean addAgree(AgreeVo agreeVo) {
        //1.获取当前用户
        int userId = agreeVo.getUserId();
        //2.判断是否已点赞
        String key ="文章"+agreeVo.getPaperId();
        //根据是否可以存到redis中判断是否点赞
        Boolean member = stringRedisTemplate.opsForSet().isMember(key, userId + "");
        //没有被点赞
        if(!member){
            //文章点赞数加1
            boolean b = paperMapper.addAgree(agreeVo.getPaperId());
            //点赞中间表待加1
            //将数据存到redis中
            if(b){
                stringRedisTemplate.opsForSet().add(key, userId + "");
            }

            return true;
        }else {
            //被点赞，说明要取消点赞
            //文章点赞数-1
            // 点赞中间表待加-1
            boolean b = paperMapper.reAgree(agreeVo.getPaperId());
            if(b){
                //删除缓存
                stringRedisTemplate.delete(key);
            }
            return false;
        }

    }


}
 